/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield 
 *
 * This library is open source and may be redistributed and/or modified under  
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 * OpenSceneGraph Public License for more details.
*/

/*  HUD Camera
*   Written by Andrew Moore
*   Arctic Region Supercomputering Center (ARSC)
*   Created June 18, 2009
*   Last Revised October 15, 2009
*
*
*  The functions defined here create a HUD that will be implemented with the customized viewer.
*
*	Changes:
*
*		8/10/2009: 
*			-Changed the projection matrix so that the HUD is more centered in the screen.  -AM
*			-Added the getWidth and getHeight functions, for getting the width and height from the
*			 OSG_WINDOW environmental variable.  Not in use yet.		-AM
*		9/02/2009:
*			-Added the ability to scroll through descriptive text.  Right now it is mapped to the 'b' key.  This can be changed at a later date.
		10/15/2009
			-Removed deprecated routines getWidth(std::string) and getHeight(std::string)
			-Fixed problem with duplicate key-presses in the updateText() function.
			-Rewrote updateText(), eliminating the for loop.
			-Fixed the problem with looping back to the beginning of the text file with updateText()
*	
*	To do:
*		- Convert everything into classes, and eliminate the global variables.
*/

#include "/import/home/u1/uaf/amoore/Working/Headers/HUD.h"

/*	Global Variables 	*/

osgText::Text* progDescription = new osgText::Text;	// Pointer to hold a description of the application.
osgText::String* description = new osgText::String;	// Temporary variable for parsing the application's description.
std::vector<std::string> tempString;	// Vector that will hold the edited strings that are extracted from the data file.
int currentLine = 0;			// Tells  we're looking at the (0)title, (1)author or (>=2)description of the application.
std::string programDescription = "";	// Temporary string to hold the description of the application.

/*				*/

void updateText()
{

	//osg::notify(osg::NOTICE) << " currentLine = " << currentLine << std::endl;
	std::string temp = "^";
	programDescription = "";
	static int i = currentLine + 3;
	
	while (temp[0] == '^')
	{
		i++;
		if (i < tempString.size()-1)
		{
			temp = tempString[i];
		}
		else
		{
			currentLine = 0;
			i = currentLine + 2;
			temp = tempString[i];
		}
	}
	if (i < tempString.size())
	{
		//osg::notify(osg::NOTICE) << "i = " << i << std::endl;
		//osg::notify(osg::NOTICE) << "tempstring[i] == " << tempString[i] << std::endl;
		temp = tempString[i];
		//osg::notify(osg::NOTICE) << "temp[0] " << temp[0] << std::endl;	*/
		if (temp[0] != '^')
		{
			temp = stringFormatCorrector(tempString[i],1);	
			//osg::notify(osg::NOTICE) << "temp = " << temp << std::endl;
			
			programDescription = temp;
			description -> set(programDescription);			// Set description equal to the fixed program description.
			//osg::notify(osg::NOTICE) << "the new description should be:  " << programDescription << std::endl;
			progDescription -> setText(*description);
			
		}
		/*else
		{
			currentLine = i;
			i = tempString.size();	
		}*/
		//osg::notify(osg::NOTICE) << "CurrentLine = " << currentLine << std::endl;
	}
	else
		i++;
		//osg::notify(osg::NOTICE) << "CurrentLine = " << i << std::endl;
	return;
}

struct HUDEventHandler : public osgGA::GUIEventHandler //Structure contains the function for showing/hiding the HUD
{

	HUDEventHandler (int key,osg::ref_ptr<osg::Group> group):	//Constructor for the HUDDisplay structure
        	_key(key),
		_group(group)
         {}

	bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&) //Function that performs the show/hide actions.
	{
		bool handle = false;  //Variable tracks whether event has occured.

        	if (!ea.getHandled()) // If the event is not something that is already handled.
		{ 
        		switch(ea.getEventType()) //Test the type of event that has occured.
        		{
				case(osgGA::GUIEventAdapter::KEYUP):  //Key has been pressed.
     				{
        		        	if (ea.getKey() == _key)  //Tests to see if the key is one of the defined keys recognized by the program.
        		        	{
						//osg::notify(osg::NOTICE)<<"event handler "<<std::endl;   //Outputs event notification to terminal.	

						if (_key == 0x61)  //Checks to see if the key pressed is the a key.
						{
							if ((_group -> getNodeMask()) == 0x0)
								_group -> setNodeMask(0x1);	// Make the HUD visible
							else
								_group -> setNodeMask(0x0);	// Hide the HUD.
						}
						else if (_key == 0x62) // Checks to see if the key pressed is the 'b' key
						{
							updateText();
							
						}
					}
					break;
				}
        			default:	// Happens when the user hasn't pressed the key we're watching for, just does nothing.
				{
					break;
				}
        		}
		}
        	return handle;	// Returns whether the key was handled or not.
	}
	private:
		int                     _key;
		osg::ref_ptr<osg::Group> _group;
};

osg::Camera* createHUD(std::string file)  	// This functions creates the HUD display, accepts a string containing the path to the data file.
{
	const std::string fileName = file;	// Get the name of the file containing the information for the HUD.
	osgDB::ifstream statisticsFile;		// This will hold some of the statistics needed by the HUD
	std::string programName = "";		// Temporary string to hold the name of the application.
	std::string osgWindow = "";		// Will hold the value of the OSG_WINDOW environmental variable.
	std::string programAuthor = "";		// Temporary string to hold the author(s) of the application.
	std::string temp = "";			// General purpose temporary string variable.
	int lineCount = 0;			// Tells  we're looking at the (0)title, (1)author or (>=2)description of the application.
	int windowWidth = 0;				// Will hold the width of the window..
	int windowHeight = 0;				// Will hold the height of the window.
	
	//Create a camera to set up the projection and model view matrices, and the subgraph.

	osg::Camera* camera = new osg::Camera;

	camera -> setProjectionMatrix(osg::Matrix::ortho2D(-465,1280,0,1024));

	//Set the view matrix

	camera -> setReferenceFrame(osg::Transform::ABSOLUTE_RF);
	camera -> setViewMatrix(osg::Matrix::identity());

	//Clear the depth buffer

	camera -> setClearMask(GL_DEPTH_BUFFER_BIT);

	//Draw the subgraph after the main camera view

	camera -> setRenderOrder(osg::Camera::POST_RENDER);

	//Make sure camera doesn't grab event focus from viewers main camera(s)

	camera -> setAllowEventFocus(false);

	//Add a subgraph to the camera
	{
		osg::Geode* geode = new osg::Geode();

		std::string timesFont("fonts/arial.ttf");	// Set the font that the HUD will be using.

		//Turn lighting off for the text and disable depth test to ensure always on top.

		osg::StateSet* stateset = geode -> getOrCreateStateSet();
		stateset -> setMode(GL_LIGHTING,osg::StateAttribute::OFF);	//Turn off the OpenGL lighting.
		
		osg::Vec3 position(0.0f,50.0f,0.0f);

		osg::Vec3 delta(0.0f,50.0f,0.0f);

		if (fileExists(fileName))	// The file exists and is accessible, proceed.
		{
			//osg::notify(osg::NOTICE) << "The file exists." << std::endl;

			statisticsFile.open(fileName.c_str(),std::ios::in); //Open the file containing the 

			while (!statisticsFile.eof())  //Loop through the file and initialize the vector.
			{
				temp = "";
				getline(statisticsFile,temp);
				tempString.push_back(temp + "\n");
			}
	
			statisticsFile.close(); //Close the file, we're finished with it.

			for (unsigned int i = 0; i < tempString.size()-1; i++)	// Loop through the data and call the formatting function for each line.
			{
				// Send the string to the stringFormatCorrector to remove the leading descriptions.
				temp = stringFormatCorrector(tempString[i],lineCount);	

				if (lineCount < 0)	// There is no such thing as -1 lines :P
				{
					osg::notify(osg::NOTICE) << "Something went terribly wrong." << std::endl;
					exit(1);
				}
				else
				{
					if (lineCount == 0)	// The application name is the first line.
						programName += temp;
					else if (lineCount == 1)	// The application's author(s) are the second line.
						programAuthor += temp;
					else if (temp[0] != '^')	// Everything else is the description.
						programDescription += temp;
				}
				lineCount++;
				
				//osg::notify(osg::NOTICE) << "Tempstring:" << tempString[i] << std::endl;
			}
			osgText::String* name = new osgText::String;		// Temporary variable for parsing the application's name1200
			osgText::String* author = new osgText::String;		// Temporary variable for parsing the application's author(s)
			//osgText::String* description = new osgText::String;	// Temporary variable for parsing the application's description.

			osgText::Text* progName = new osgText::Text;		// Pointer to hold the name of the tapplication.
			osgText::Text* progAuthor = new osgText::Text;		// Pointer to hold the author(s) of the application.
			

			name -> set(programName);				// Set name equal to the fixed program name.
			author -> set(programAuthor);				// Set author(s) equal to the fixed program author(s)
			description -> set(programDescription);			// Set description equal to the fixed program description.

			// Add our name, author and description nodes to the geode node.
			geode -> addDrawable(progName);
			geode -> addDrawable(progAuthor);
			geode -> addDrawable(progDescription);

			//  Set up the program description.
			progDescription -> setFont(timesFont);
			progDescription -> setPosition(position);
			progDescription -> setCharacterSize(25);
			progDescription -> setText(*description);

			position += delta;	// Move the location of the next text object up by delta pixels

			// Set up the program author.
			progAuthor -> setFont(timesFont);
			progAuthor -> setPosition(position);
			progAuthor -> setCharacterSize(25);
			progAuthor -> setText(*author);

			position += delta;	// Move the location of the next text object up by delta pixels
	
			// Set up the program name.
			progName -> setFont(timesFont);
			progName -> setAlignment(osgText::Text::CENTER_CENTER); // Supposed to align the title, for some reason aligns everything else (?)
			progName -> setPosition(position);
			progName -> setCharacterSize(30);
			progName -> setColor(osg::Vec4(0.5f,0.9f,0.5f,1.0f));
			progName -> setText(*name);
			progName -> setBackdropColor(osg::Vec4(00.f,0.0f,0.0f,1.0f)); // Set the color for the shadowing of the name, makes it stand out more.
			progName -> setBackdropType(osgText::Text::DROP_SHADOW_BOTTOM_RIGHT);	// Set the posinclude <osg/Material>
	
			progName -> setBackdropImplementation(osgText::Text::POLYGON_OFFSET);	// Set the type of backdrop implementation to use.
			progName -> setBackdropOffset(.20);  // Offset the backdrop by 20%.

			position += delta;	// Move the location of the next text object up by delta pixels	
		}
		else	// Opening the file failed.
		{
			osg::notify(osg::NOTICE) << fileName << " does not exist or you do not have permission to open it." << std::endl;
		}
		{
			osg::BoundingBox bb;	//Create a box that will represent the boundaries of the HUD
			
			//Resize the bounding box based on the number of drawable objects present in the scene.
		
			for (unsigned int i = 0; i < geode -> getNumDrawables(); ++i) 
				bb.expandBy(geode -> getDrawable(i) -> getBound());

			osg::Geometry* geom = new osg::Geometry;  //Create the geometry of our HUD
			
			osg::Vec3Array* vertices = new osg::Vec3Array; //Create a vector that will hold the coordinates of the HUD's vertices.
			float depth = bb.zMin()-0.1;


			//Load the vertices into the vector, based on the dimensions of the bounding box.

			vertices -> push_back(osg::Vec3((bb.xMin()),(bb.yMax() + 20),depth)); 
			vertices -> push_back(osg::Vec3((bb.xMin()),(bb.yMin() - 20),depth));
			vertices -> push_back(osg::Vec3((bb.xMax() + 20),(bb.yMin() - 20),depth));
			vertices -> push_back(osg::Vec3((bb.xMax() + 20),(bb.yMax() + 20),depth));
			geom -> setVertexArray(vertices); //Attach the vertices to the vertex array.

			osg::Vec3Array* normals = new osg::Vec3Array; //Vector will hold the normals associated with the HUD.

			normals -> push_back(osg::Vec3(0.0f,0.0f,1.0f));
			geom -> setNormalArray(normals);
			geom -> setNormalBinding(osg::Geometry::BIND_OVERALL); 

			osg::Vec4Array* colors = new osg::Vec4Array;  			//Set the color information

			colors -> push_back(osg::Vec4(1.0f,1.0f,0.8f,0.2f));		//Set the color of the HUD
			geom -> setColorArray(colors); 					//Initialize the color array
			geom -> setColorBinding(osg::Geometry::BIND_OVERALL); 		//Make sure each vertex has only one color.

			geom -> addPrimitiveSet(new osg::DrawArrays(GL_QUADS,0,4));	//Draw the primitive that will be the HUD

			osg::StateSet* stateset = geom -> getOrCreateStateSet();	//Set the OpenGL states that the HUD will possess.
			stateset -> setMode(GL_BLEND,osg::StateAttribute::ON);		//Set blending to on.
			stateset -> setRenderingHint(osg::StateSet::TRANSPARENT_BIN);	//Tell the viewer to render the HUD transparently.

			geode -> addDrawable(geom); //Add the HUD geometry to the geode node.
		}
		camera -> addChild(geode); //Attach the geode to the camera.
	}
	return camera; //Give the camera back to the calling function.
}

